package com.ybear.ybutils.utils.notification;

import android.app.AppOpsManager;
import android.app.Application;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.Build;
import android.provider.Settings;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import com.ybear.ybutils.utils.AppUtil;
import com.ybear.ybutils.utils.ObjUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.List;

/**
 * 通知栏
 * <a href="https://developer.android.google.cn/guide/topics/ui/notifiers/notifications?hl=zh-cn">通知概览</a>
 */
public class NotificationX {
    private NotificationManager mNotifyManage;

    public static NotificationX get() { return NotificationX.HANDLER.INSTANCE; }
    private static final class HANDLER {
        private static final NotificationX INSTANCE = new NotificationX();
    }

    public void init(@NonNull Application app) {
        mNotifyManage = (NotificationManager) app.getSystemService(
                Context.NOTIFICATION_SERVICE
        );
    }

    public NotificationManager getNotificationManager() { return mNotifyManage; }

    /**
     创建一个通知需要的Notification
     @param build       通知数据
     @return            {@link Notification}
     */
    @Nullable
    public Notification createNotification(Context context, NotificationBuild build) {
        if( context == null ) return null;
        if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) {
            addChannel( build.getChannel().getNotificationChannel() );
        }
        return build.build( context );
    }

    public boolean show(Context context, int id, Notification notification) {
        if( notification == null ) return false;
        //检查您的应用能否发送通知
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !areNotificationsEnabled( context ) ) {
            return false;
        }
        try {
            getNotificationManager().notify( id, notification );
            return true;
        }catch(Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     展示一个通知
     @param id      通知id
     @param build   通知数据
     @return        展示结果
     */
    public boolean showNotification(Context context, int id, NotificationBuild build) {
        return show( context, id, createNotification( context, build ) );
    }

    /**
     展示一个通知
     @param id              通知id
     @param channelId       渠道id（8.0特性）
     @param channelName     渠道名（8.0特性）
     @param importance      通知重要性（8.0特性）
     @param smallIcon       小图标（通知左上角图标）
     @param largeIcon       大图标（内容右边的图标）
     @param title           标题
     @param content         内容
     @param openIntent      点击通知时的处理事件
     @param deleteIntent    取消/清空通知时的处理事件
     @param autoCancel      是否点击后自动取消
     @param isShowBadge     是否展示桌面图标小圆点
     @return                展示结果
     */
    public boolean showNotification(Context context,
                                    int id,
                                    @NonNull String channelId,
                                    @Nullable String channelName,
                                    @Importance int importance,
                                    int smallIcon,
                                    int largeIcon,
                                    @Nullable CharSequence title,
                                    @Nullable CharSequence content,
                                    @Nullable Intent openIntent,
                                    @Nullable Intent deleteIntent,
                                    boolean autoCancel,
                                    boolean isShowBadge) {
        NotificationBuild build = new NotificationBuild(
                channelId, channelName, importance, isShowBadge
        );
        return showNotification( context, id, build
                .setSmallIcon( smallIcon )
                .setLargeIcon( largeIcon ).setTitle( title )
                .setContent( content )
                .setOpenIntent( openIntent )
                .setDeleteIntent( deleteIntent )
                .setAutoCancel( autoCancel )
        );
    }

    /**
     展示一个通知
     @param id              通知id
     @param smallIcon       小图标（通知左上角图标）
     @param largeIcon       大图标（内容右边的图标）
     @param title           标题
     @param content         内容
     @param openIntent      点击通知时的处理事件
     @param deleteIntent    取消/清空通知时的处理事件
     @param autoCancel      是否点击后自动取消
     @param isShowBadge     是否展示桌面图标小圆点
     @return                展示结果
     */
    public boolean showNotification(Context context,
                                    int id,
                                    int smallIcon,
                                    int largeIcon,
                                    @Nullable CharSequence title,
                                    @Nullable CharSequence content,
                                    @Nullable Intent openIntent,
                                    @Nullable Intent deleteIntent,
                                    boolean autoCancel,
                                    boolean isShowBadge) {
        if( context == null ) return false;
        String pkgName = AppUtil.getPackageName( context );
        String channelId = pkgName == null ? "com.ybear.ybutils_" + hashCode() : pkgName;
        return showNotification(
                context, id, channelId, null, Importance.UNSPECIFIED,
                smallIcon, largeIcon, title, content, openIntent, deleteIntent,
                autoCancel, isShowBadge
        );
    }

    /**
     展示一个通知
     @param id              通知id
     @param smallIcon       小图标（通知左上角图标）
     @param title           标题
     @param content         内容
     @param openIntent      点击通知时的处理事件
     @param deleteIntent    取消/清空通知时的处理事件
     @param autoCancel      是否点击后自动取消
     @param isShowBadge     是否展示桌面图标小圆点
     @return                展示结果
     */
    public boolean showNotification(Context context,
                                    int id,
                                    int smallIcon,
                                    @Nullable CharSequence title,
                                    @Nullable CharSequence content,
                                    @Nullable Intent openIntent,
                                    @Nullable Intent deleteIntent,
                                    boolean autoCancel,
                                    boolean isShowBadge) {
        return showNotification(
                context, id, smallIcon, 0, title, content, openIntent, deleteIntent,
                autoCancel, isShowBadge
        );
    }

    /**
     展示一个通知
     @param id              通知id
     @param smallIcon       小图标（通知左上角图标）
     @param title           标题
     @param content         内容
     @param openIntent      点击通知时的处理事件
     @param deleteIntent    取消/清空通知时的处理事件
     @param isShowBadge     是否展示桌面图标小圆点
     @return                展示结果
     */
    public boolean showNotification(Context context,
                                    int id,
                                    int smallIcon,
                                    @Nullable CharSequence title,
                                    @Nullable CharSequence content,
                                    @Nullable Intent openIntent,
                                    @Nullable Intent deleteIntent,
                                    boolean isShowBadge) {
        return showNotification(
                context, id, smallIcon, title, content, openIntent, deleteIntent,
                true, isShowBadge
        );
    }

    /**
     展示一个通知
     @param id              通知id
     @param title           标题
     @param content         内容
     @param openIntent      点击通知时的处理事件
     @param deleteIntent    取消/清空通知时的处理事件
     @param isShowBadge     是否展示桌面图标小圆点
     @return                展示结果
     */
    public boolean showNotification(Context context,
                                    int id,
                                    @Nullable CharSequence title,
                                    @Nullable CharSequence content,
                                    @Nullable Intent openIntent,
                                    @Nullable Intent deleteIntent,
                                    boolean isShowBadge) {
        return showNotification(
                context, id, 0, title, content, openIntent, deleteIntent,
                true, isShowBadge
        );
    }

    /**
     展示一个通知
     @param id              通知id
     @param smallIcon       小图标（通知左上角图标）
     @param title           标题
     @param content         内容
     @param openIntent      点击通知时的处理事件
     @param isShowBadge     是否展示桌面图标小圆点
     @return                展示结果
     */
    public boolean showNotification(Context context,
                                    int id,
                                    int smallIcon,
                                    @Nullable CharSequence title,
                                    @Nullable CharSequence content,
                                    @Nullable Intent openIntent,
                                    boolean isShowBadge) {
        return showNotification(
                context, id, smallIcon, title, content, openIntent, null, isShowBadge
        );
    }

    /**
     展示一个通知
     @param id              通知id
     @param title           标题
     @param content         内容
     @param openIntent      点击通知时的处理事件
     @param isShowBadge     是否展示桌面图标小圆点
     @return                展示结果
     */
    public boolean showNotification(Context context,
                                    int id,
                                    @Nullable CharSequence title,
                                    @Nullable CharSequence content,
                                    @Nullable Intent openIntent,
                                    boolean isShowBadge) {
        return showNotification(
                context, id, 0, title, content, openIntent, null, isShowBadge
        );
    }

    /**
     展示一个通知
     @param id              通知id
     @param smallIcon       小图标（通知左上角图标）
     @param title           标题
     @param content         内容
     @param isShowBadge     是否展示桌面图标小圆点
     @return                展示结果
     */
    public boolean showNotification(Context context,
                                    int id,
                                    int smallIcon,
                                    @Nullable CharSequence title,
                                    @Nullable CharSequence content,
                                    boolean isShowBadge) {
        return showNotification( context, id, smallIcon, title, content, null, isShowBadge );
    }

    /**
     展示一个通知
     @param id              通知id
     @param title           标题
     @param content         内容
     @param isShowBadge     是否展示桌面图标小圆点
     @return                展示结果
     */
    public boolean showNotification(Context context,
                                    int id,
                                    @Nullable CharSequence title,
                                    @Nullable CharSequence content,
                                    boolean isShowBadge) {
        return showNotification( context, id, 0, title, content, isShowBadge );
    }

    /**
     取消指定通知
     @param id      通知id
     */
    public void cancel(int id) { getNotificationManager().cancel( id ); }

    /**
     清空全部通知
     */
    public void cancelAll() { getNotificationManager().cancelAll(); }

    /**
     * 创建渠道组
     * @param channelId     渠道组id
     * @param channelName   渠道组名称
     * @return              this
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    public NotificationChannelGroup createChannelGroup(@NonNull String channelId,
                                                       @NonNull CharSequence channelName) {
        return new NotificationChannelGroup(channelId, channelName);
    }

    /**
     * 添加创建的渠道
     * @param group         渠道组{@link #createChannelGroup(String, CharSequence)}
     * @return              this
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    public NotificationX addChannelGroup(NotificationChannelGroup group) {
        getNotificationManager().createNotificationChannelGroup( group );
        return this;
    }

    /**
     * 添加创建的渠道
     * @param groups        渠道组列表
     * @return              this
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    public NotificationX addChannelGroups(List<NotificationChannelGroup> groups) {
        getNotificationManager().createNotificationChannelGroups( groups );
        return this;
    }

    /**
     * 删除渠道组
     * @param channelId     渠道id
     * @return              this
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    public NotificationX deleteChannelGroup(String channelId) {
        getNotificationManager().deleteNotificationChannelGroup( channelId );
        return this;
    }

    /**
     * 创建渠道
     * @param channelId     渠道id
     * @param channelName   渠道名称
     * @param importance    重要性 {@link Importance}
     * @return              this
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    public NotificationChannel createChannel(@NonNull String channelId,
                                             @NonNull CharSequence channelName,
                                             @Importance int importance) {
        return new NotificationChannel( channelId, channelName, importance );
    }

    /**
     * 添加创建的渠道
     * @param channel       渠道{@link #createChannel(String, CharSequence, int)}
     * @return              this
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    public NotificationX addChannel(NotificationChannel channel) {
        getNotificationManager().createNotificationChannel( channel );
        return this;
    }

    /**
     * 添加创建的渠道
     * @param channels       渠道列表
     * @return              this
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    public NotificationX addChannels(List<NotificationChannel> channels) {
        getNotificationManager().createNotificationChannels( channels );
        return this;
    }

    /**
     * 删除渠道
     * @param channelId     渠道id
     * @return              this
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    public NotificationX deleteChannel(String channelId) {
        getNotificationManager().deleteNotificationChannel( channelId );
        return this;
    }

    @RequiresApi(api = Build.VERSION_CODES.S)
    public boolean areBubblesEnabled() {
        return mNotifyManage != null && mNotifyManage.areBubblesEnabled();
    }

    /**
     * 检查是否允许通知
     */
    public boolean areNotificationsEnabled(Context context) {
        if( mNotifyManage == null || context == null ) return false;
        //高版本检查
        if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ) {
            return mNotifyManage.areNotificationsEnabled();
        }
        //低版本检查
        try {
            AppOpsManager aom = (AppOpsManager) context.getSystemService( Context.APP_OPS_SERVICE );
            ApplicationInfo appInfo = context.getApplicationInfo();
            String pkg = context.getApplicationContext().getPackageName();
            Class<?> appOpsClass = Class.forName( AppOpsManager.class.getName() );
            int opPostNoticeValue = ObjUtils.parseInt(
                    appOpsClass.getDeclaredField("OP_POST_NOTIFICATION").get( Integer.class )
            );
            int checkOpNoThrow = ObjUtils.parseInt(
                    appOpsClass.getMethod(
                            "checkOpNoThrow", Integer.TYPE, Integer.TYPE, String.class
                    ).invoke( aom, opPostNoticeValue, appInfo.uid, pkg )
            );
            return checkOpNoThrow == AppOpsManager.MODE_ALLOWED;
        } catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException |
                 InvocationTargetException | IllegalAccessException | RuntimeException e) {
            return true;
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.Q)
    public boolean areNotificationsPaused() {
        return mNotifyManage != null && mNotifyManage.areNotificationsPaused();
    }

    @RequiresApi(api = Build.VERSION_CODES.Q)
    public boolean areBubblesAllowed() {
        return mNotifyManage != null && mNotifyManage.areBubblesAllowed();
    }


    /**
     * 通知权限申请
     */
    public boolean requestNotification(Context context) {
        if( context == null ) return false;
        try {
            Intent intent = new Intent();
            String pkgName = context.getPackageName();
            //跳到通知栏设置界面
            if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                intent.setAction( Settings.ACTION_APP_NOTIFICATION_SETTINGS );
                intent.putExtra( Settings.EXTRA_APP_PACKAGE, pkgName );
            } else {
                intent.setAction( "android.settings.APP_NOTIFICATION_SETTINGS" );
                intent.putExtra( "app_package", pkgName );
                intent.putExtra( "app_uid", context.getApplicationInfo().uid );
            }
            context.startActivity( intent );
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}
